package com.gmrz.uaf.crypto.internal.handlers;

import java.security.GeneralSecurityException;
import java.security.SecureRandom;
import java.util.Arrays;

import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;

import org.apache.commons.lang3.ArrayUtils;

import com.gmrz.uaf.common.UAFRuntimeException;
import com.gmrz.uaf.crypto.CryptoConstants;
import com.gmrz.uaf.crypto.spi.handlers.CipherHandler;

public abstract class BaseCipherHandler implements CipherHandler {
	protected String transformation = null;

	protected abstract SecretKey getKey();

	public byte[] encrypt(byte[] clearData) throws GeneralSecurityException {
		byte[] iv = null;
		IvParameterSpec ivSpec = null;
		Cipher cipher = Cipher.getInstance(this.transformation, CryptoConstants.BOUNCY_CASTLE_PROVIDER_NAME);

		iv = generateIV(cipher.getBlockSize());
		ivSpec = new IvParameterSpec(iv);
		cipher.init(1, getKey(), ivSpec);
		return ArrayUtils.addAll(iv, cipher.doFinal(clearData));
	}

	public byte[] decrypt(byte[] cipherText) throws GeneralSecurityException {
		Cipher cipher = null;
		byte[] iv = null;
		IvParameterSpec ivSpec = null;
		cipher = Cipher.getInstance(this.transformation, CryptoConstants.BOUNCY_CASTLE_PROVIDER_NAME);

		if (cipherText == null) {
			throw new UAFRuntimeException("Cipher text is empty.");
		}

		if (cipherText.length < cipher.getBlockSize()) {
			throw new UAFRuntimeException("Cipher text is not of the desired length.");
		}
		iv = Arrays.copyOfRange(cipherText, 0, cipher.getBlockSize());
		ivSpec = new IvParameterSpec(iv);

		byte[] cipherTextMinusIv = Arrays.copyOfRange(cipherText, iv.length, cipherText.length);

		cipher.init(2, getKey(), ivSpec);

		return cipher.doFinal(cipherTextMinusIv);
	}

	byte[] generateIV(int blockSize) {
		byte[] ivBytes = new byte[blockSize];
		SecureRandom random = new SecureRandom();
		random.nextBytes(ivBytes);
		return ivBytes;
	}
}
